const canvas = document.getElementById('canvas') as HTMLCanvasElement;

class Draw {
  public ctx: CanvasRenderingContext2D
  public canvasArray: Particle[] = []
  public setp: number = 10;
  public timer: NodeJS.Timer | null = null;
  constructor(public canvas: HTMLCanvasElement) {
    this.canvas = canvas;
    this.ctx = canvas.getContext('2d')
    this.ctx.fillRect(0, 0, 300, 150)
    const throttleMousemove = throttle(this.handleMousemove.bind(this), 100)
    this.canvas.addEventListener('mousemove', throttleMousemove)
  }

  createCanvas(x: number, y: number) {
    const particle = new Particle(x, y)
      .draw(this.ctx, 0);
    this.generator(particle)
    return particle
  }

  handleMousemove(event: MouseEvent) {
    const { offsetX, offsetY } = event;
    this.createCanvas(offsetX, offsetY)
  }

  generator(particle: Particle) {
    this.canvasArray.push(particle)
    this.setp = 10

    if (this.timer) return
    this.timer = setInterval(() => {
      this.ctx.clearRect(0, 0, 300, 150)
      this.ctx.fillStyle = '#000'
      this.ctx.fillRect(0, 0, 300, 150)

      for (const particle of this.canvasArray) {
        particle.vitalityParticle()
      }
      
      this.setp--
      if (this.setp === 0) {
        clearInterval(this.timer)
        this.timer = null
      }
    }, 100)
  }

}

class Particle {
  public ctx: CanvasRenderingContext2D
  public r: number
  public scaleFlag: boolean = false
  constructor(public x: number, public y: number) {
    this.x = x;
    this.y = y;
  }

  draw(ctx: CanvasRenderingContext2D, r: number) {
    if (r < 0) return
    this.ctx = ctx
    this.r = r
    ctx.fillStyle = "rgba(255,255,255," + r / 10 + ")";
    ctx.shadowBlur = r * 2 / 10;
    ctx.beginPath();
    ctx.arc(this.x, this.y, r, 0, 2 * Math.PI, false);
    ctx.closePath();
    ctx.fill();
    return this
  }

  vitalityParticle() {
    if (!this.scaleFlag) {
      this.r += 2
    } else {
      this.r -= 2
    }
    this.draw(this.ctx, this.r)
    if (this.r === 10) {
      this.scaleFlag = true
    }
    return this
  }

}

const draw = new Draw(canvas)

function throttle(func: { apply: (arg0: any, arg1: IArguments) => void; }, ms: number) {

  let isThrottled = false,
    savedArgs: IArguments,
    savedThis: any;

  function wrapper() {

    if (isThrottled) { // (2)
      savedArgs = arguments;
      savedThis = this;
      return;
    }

    func.apply(this, arguments); // (1)

    isThrottled = true;

    setTimeout(function () {
      isThrottled = false; // (3)
      if (savedArgs) {
        wrapper.apply(savedThis, savedArgs);
        savedArgs = savedThis = null;
      }
    }, ms);
  }

  return wrapper;
}